#pragma once

#include<SDL2/SDL.h>
#include<SDL2/SDL_mixer.h>
#include "textutil.hpp"
#include "Color.hpp"

#include<unordered_map>
#include<unordered_set>
#include<vector>
#include<list>
#include<string>

typedef struct{
    int w;
    unsigned int len;
}U8Unit;

typedef void(*TaskCallback)(void*);

using namespace std;

class FileNotFoundException:exception{};

const int _input_delay=10,_menu_height=20,_menu_item_height=24;
const int quad_level_max=4;
const int quad_max=16;

class Font{
    int _size;
    unordered_map<int,unordered_map<int,SDL_Texture*>*>* _cache;//color强转int u8字串转成int
    public:
    TTF_Font* _font;
    const int size();
    const int height();//渲染字体中最高的
    //获取或新渲染一个u8字，不需要析构(缓存在对象内部)
    SDL_Texture* RenderUnit(SDL_Renderer* renderer,int u8_value,SDL_Color color);
    SDL_Surface* RenderUTF8(const char* text,SDL_Color color,int size);
    //清理缓存
    void Clear();
    //返回设置之前的值
    int SetOutline(int size);
    //返回设置之前的值
    int SetSize(int size);

    //如果字体不存在，抛出FileNotFoundException
    Font(const char* path,int init_size);
    ~Font();
};

extern SDL_Cursor *edit_cursor,*arrow_cursor,*button_cursor;
extern Font* default_font;
extern Uint32 frame_delay;
extern SDL_Renderer* gRender;

class Control;

typedef struct Task{
    void(*func)(void*);
    void(*finish)(void*);
    unsigned int cur,max;
    void* arg,*finish_arg;
    bool every,forever;
}Task;

class Renderer{
    public:
    SDL_Renderer *render;
    Renderer(SDL_Window* window,SDL_BlendMode mode=SDL_BLENDMODE_BLEND);
    ~Renderer();
    void Update();
    void SetColor(SDL_Color color);
    void DrawLine(SDL_Point& p1,SDL_Point& p2);
    void DrawRect(SDL_Rect& rect);
    void DrawPoint(SDL_Point p);
    void FillRect(SDL_Rect& rect);
    void DrawTexture(SDL_Rect* src,SDL_Rect* dst,SDL_Texture* texture);
    void DrawTexture(SDL_Rect* src,SDL_Rect* dst,SDL_Texture* texture,
    double angle,SDL_RendererFlip flip);
    //target==NULL->窗口
    void SetTarget(SDL_Texture* target=NULL);
    void SetBlendMode(SDL_BlendMode mode);
    void Clear();
    //将指定字符串按U8单位分割后缓存，返回元数据
    vector<U8Unit>* SaveU8Units(const char* text,Font* font,SDL_Color color);
    //清空并渲染区间内的所有u8单元，宽到了就停 返回渲染停止时的位置
    const char* RenderHorizontalText(SDL_Texture* _t,SDL_Color background,Font* font,const char* begin,const char* end,SDL_Color color);
    const char* RenderHighlightHorizontalText(SDL_Texture* _t,SDL_Color background,Font* font,const char* begin,
    const char* end,SDL_Color text_color,
    const char* h_begin,const char* h_end,SDL_Color high_color);
    SDL_Texture* RenderText(Font* font,const char* text,SDL_Color color);
    SDL_Texture* CreateTexture(int w,int h,Uint32 format=SDL_PIXELFORMAT_RGBA8888,int access=SDL_TEXTUREACCESS_TARGET);
    //找不到返回NULL
    SDL_Texture* LoadTexture(const char* path);
    //从内存读取图像纹理
    SDL_Texture* LoadTextureRW(const char* buffer,int size);
    //将一个u8单元传入后返回单个渲染字 不需要析构(在字体内部已缓存记录)
    SDL_Texture* RenderSingleText(Font* font,int u8_value,SDL_Color color);
    SDL_Texture* GetTarget();
};
class MenuList;
//创建后自动注册到全局表中，析构后自动从全局表移除 支持创建多个

class Window{
    SDL_Surface* icon;
    unsigned int _id;//勿动
    protected:
    SDL_Window* window;
    unordered_set<Control*> focus;//框架处理正常情况下的焦点逻辑on/lose
    vector<Control*> controls;
    unordered_set<Window*> children;
    Window* parent;
    unordered_set<Task*> tasks;
    Control* _last_click;

    virtual bool _mouse_move(int x,int y);
    virtual bool _mouse_release(int x,int y,int clicks,unsigned char key);
    virtual bool _mouse_press(int x,int y,int clicks,unsigned char key);
    virtual bool _key_up(SDL_Keysym& sym);
    virtual bool _key_down(SDL_Keysym& sym);
    virtual void _draw();

    Control* GetCheck(int x,int y);
    public:
    MenuList* _input_list;
    bool not_top;
    virtual bool _close();//返回false则不关闭当前窗口
    unsigned int id();
    SDL_Color background;
    Renderer* renderer;
    Window(const char* icon,const char* title,int w,int h,bool resize,bool full,SDL_Color back=Color::White,Window* parent=NULL,SDL_BlendMode mode=SDL_BLENDMODE_BLEND);
    virtual ~Window();

    SDL_Window* GetSDLWindow();

    virtual void OnWindowResize(int w,int h);
    void Resize(int w,int h);
    void SetTitle(const char* text);
    //WindowWithMenu类支持该接口,Window类不支持
    virtual void ActiveMenu(MenuList* menu);
    void CheckMouseDown(int x,int y,int clicks,unsigned char key);
    void CheckMouseMove(int x,int y);
    void CheckMouseUp(int x,int y,int clicks,unsigned char key);
    void CheckTextInput(const char* text);
    void CheckKeyUp(SDL_Keysym& sym);
    void CheckKeyDown(SDL_Keysym& sym);
    void CheckMouseWheel(SDL_MouseWheelEvent& event);
    //task会在每一帧绘制前更新
    Task* AddTask(void(*func)(void*),void* arg,unsigned int max_frame,
    bool every=false,bool forever=false,void(*finish)(void*)=[](void*)->void{},void* finish_arg=0);
    //调用后会析构task 请在确保task不会被访问到的情况下析构
    void RemoveTask(Task* task);
    //循环直到所有非永久任务t==max
    void FinalizeTask();
    void Draw();
    void CancelFocus(Control* c);//会触发该控件的LoseFocus
    void AddFocus(Control* c);//会触发该控件的OnFocus
    //按添加顺序进行绘制，添加后析构时会自动释放所占内存 不要重复添加
    Control* AddControl(Control* c);
};

class Control{
    protected:
    bool _enabled;
    bool _visible;
    SDL_Rect _area;
    
    public:
    Window* _belong;//勿动
    int layer;
    bool resizable;
    bool draggable;
    bool pressed;//由框架设置
    void* arg;

    Control(Window* window);

    const Window* window();
    bool enable();
    bool visible();
    SDL_Rect& area();

    virtual void SetEnable(bool v);
    virtual void SetPosition(int x,int y);
    virtual void OffsetPosition(int x,int y);
    virtual void SetSize(int w,int h);
    virtual void SetVisible(bool v);

    virtual void _draw();
    virtual void _draw_edge();
    virtual void _mouse_move(int x,int y);
    virtual void _press(int x,int y,int clicks,unsigned char key);
    virtual void _release(int x,int y,int clicks,unsigned char key);
    virtual void _mouse_wheel(SDL_MouseWheelEvent& event);
    virtual void _key_down(SDL_Keysym& sym);//键盘事件
    virtual void _key_up(SDL_Keysym& sym);
    virtual void _input(const char* text);//输入
    virtual void _drag(int x,int y);
    //一般情况下仅在首次点击触发
    virtual void OnFocus();
    //一般情况下仅在区域外点击或点击另一控件才会触发
    virtual void LoseFocus();

    virtual ~Control();
};

class MenuItem{
    protected:
    Renderer* _r;
    public:
    MenuList* _l;//扩展列表
    SDL_Texture* _t;
    bool _v;//检查框用
    int _w;//用于菜单栏判断点击,勿动
    bool enabled;//为false时不会触发
    void(*func)(MenuItem*);//仅在展开菜单中触发
    virtual ~MenuItem();
    virtual bool DrawCheck();
    MenuItem(Window* window,MenuList* l,bool enabled=true,bool value=false,void(*func)(MenuItem*)
    =[](MenuItem*)->void{});
};

class MenuText:public MenuItem{
    protected:
    const char* _text;
    Font* _font;
    SDL_Color _color;
    public:
    MenuText(Window* window,const char* text,bool enabled=true,bool value=false,
    void(*func)(MenuItem*)=[](MenuItem*)->void{},
    Font* font=default_font,SDL_Color color=Color::Black,MenuList* l=NULL);
    void SetText(const char* new_text,Font* font=default_font,SDL_Color color=Color::Black);
    bool DrawCheck();
    const char* text();
    SDL_Color color();
    Font* font();
    ~MenuText()override;
};

class MenuList{
    public:
    SDL_Rect _rect;
    size_t _on,_cur;
    vector<MenuItem*>* children;
    int level;//在第几级，不要瞎填 从1开始
    int _w;
    MenuItem* Add(MenuItem* item);
    //越界抛out_of_range 返回对象需手动析构
    MenuItem* Remove(size_t index);
    //添加请使用xxx->Add(项),不要直接操作children,否则会导致可能的显示问题
    MenuList(int level,vector<MenuItem*>* children);
    ~MenuList();
};

extern unordered_map<Uint32,Window*>* windows;
extern SDL_Renderer* gRender;
extern Font* default_font;

unsigned int GetU8Len(const char* text);